package v2ray

import (
	"context"
	"encoding/json"
	"fmt"
	"gitee.com/Luna-CY/hui-hui/internal/interface/application"
	"os"
)

type Config struct {
	Inbounds  []Inbound  `json:"inbounds"`
	Outbounds []Outbound `json:"outbounds"`
	Routing   struct{}   `json:"-"`
	Policy    struct{}   `json:"-"`
	Transport struct{}   `json:"-"`
	Log       struct{}   `json:"-"`
	Stats     struct{}   `json:"-"`
}

type Inbound struct {
	Tag            string          `json:"tag,omitempty"`
	Listen         string          `json:"listen"`
	Port           int             `json:"port"`
	Protocol       string          `json:"protocol"`
	Settings       any             `json:"settings"`
	StreamSettings *StreamSettings `json:"streamSettings,omitempty"`
}

type Outbound struct {
	Tag            string          `json:"tag,omitempty"`
	SendThrough    string          `json:"sendThrough,omitempty"`
	Protocol       string          `json:"protocol"`
	Settings       any             `json:"settings"`
	StreamSettings *StreamSettings `json:"streamSettings,omitempty"`
	ProxySettings  *ProxySettings  `json:"proxySettings,omitempty"`
}

type ProxySettings struct {
	Tag string `json:"tag,omitempty"`
}

type StreamTlsSettings struct {
	ServerName string `json:"serverName,omitempty"`
}

type StreamTcpSettings struct {
	Header struct {
		Type string `json:"type,omitempty"`
	} `json:"header,omitempty"`
}

type StreamKcpSettings struct {
	Mtu              int  `json:"mtu,omitempty"`
	Tti              int  `json:"tti,omitempty"`
	UplinkCapacity   *int `json:"uplinkCapacity,omitempty"`
	DownlinkCapacity *int `json:"downlinkCapacity,omitempty"`
	Congestion       bool `json:"congestion,omitempty"`
	ReadBufferSize   int  `json:"readBufferSize,omitempty"`
	WriteBufferSize  int  `json:"writeBufferSize,omitempty"`
	Header           struct {
		Type string `json:"type,omitempty"`
	} `json:"header,omitempty"`
}

type StreamWebsocketSettings struct {
	Path    string            `json:"path,omitempty"`
	Headers map[string]string `json:"headers,omitempty"`
}

type StreamDomainSocketSettings struct {
	Path string `json:"path,omitempty"`
}

type StreamHttp2Settings struct {
	Host []string `json:"host,omitempty"`
	Path string   `json:"path,omitempty"`
}

type StreamQuicSettings struct {
	Security string `json:"security,omitempty"`
	Key      string `json:"key,omitempty"`
	Header   struct {
		Type string `json:"type,omitempty"`
	} `json:"header,omitempty"`
}

type StreamSettings struct {
	Network      string                      `json:"network"`
	Security     string                      `json:"security"`
	TlsSettings  *StreamTlsSettings          `json:"tlsSettings,omitempty"`
	TcpSettings  *StreamTcpSettings          `json:"tcpSettings,omitempty"`
	KcpSettings  *StreamKcpSettings          `json:"kcpSettings,omitempty"`
	WsSettings   *StreamWebsocketSettings    `json:"wsSettings,omitempty"`
	DsSettings   *StreamDomainSocketSettings `json:"dsSettings,omitempty"`
	HttpSettings *StreamHttp2Settings        `json:"httpSettings,omitempty"`
	QuicSettings *StreamQuicSettings         `json:"quicSettings,omitempty"`
}

type DokodemoDoorInbound struct {
	Address        string `json:"address,omitempty"`
	Port           int    `json:"port,omitempty"`
	Network        string `json:"network,omitempty"`
	Timeout        int64  `json:"timeout,omitempty"`
	FollowRedirect bool   `json:"followRedirect,omitempty"`
	UserLevel      int    `json:"userLevel,omitempty"`
}

type HttpAccount struct {
	Username string `json:"username"` // 用户名
	Password string `json:"password"` // 密码
}

type HttpInbound struct {
	Timeout          int64         `json:"timeout"`
	AllowTransparent bool          `json:"allowTransparent"`
	Accounts         []HttpAccount `json:"accounts"`
	UserLevel        int           `json:"userLevel"`
}

type MTProtoUser struct {
	Email  string `json:"email"`
	Secret string `json:"secret"`
	Level  int    `json:"level"`
}

type MTProtoInbound struct {
	Users []MTProtoUser `json:"users"`
}

type ShadowSocksInbound struct {
	Email    string `json:"email"`
	Method   string `json:"method"`
	Password string `json:"password"`
	OTA      bool   `json:"ota"`
	Network  string `json:"network"`
}

type SocksInbound struct {
	Auth      string        `json:"auth"`
	Accounts  []HttpAccount `json:"accounts"`
	UDP       bool          `json:"udp"`
	Ip        string        `json:"ip"`
	UserLevel int           `json:"userLevel"`
}

type VMessInbound struct {
	Clients                   []VMessInboundClient `json:"clients"`
	DisableInsecureEncryption bool                 `json:"disableInsecureEncryption,omitempty"`
}

type VMessInboundClient struct {
	Id      string `json:"id"`
	Level   int    `json:"level"`
	AlterId int    `json:"alterId"` // 固定0
	Email   string `json:"email"`
}

type BlackholeOutbound struct {
	Response struct {
		Type string `json:"type"`
	} `json:"response"`
}

type DNSOutbound struct {
	Network string `json:"network"`
	Address string `json:"address"`
	Port    int    `json:"port"`
}

type HttpServer struct {
	Address string        `json:"address"` // 服务器地址，不能为空
	Port    int           `json:"port"`    // 服务器端口，不能为空
	Users   []HttpAccount `json:"users"`   // 用户列表，可以为空，为空时不进行身份验证
}

type HttpOutbound struct {
	Servers []HttpServer `json:"servers"`
}

type ShadowSocksServer struct {
	Email    string `json:"email"`
	Address  string `json:"address"`
	Port     int    `json:"port"`
	Method   string `json:"method"`
	Password string `json:"password"`
	OTA      bool   `json:"ota"`
	Level    int    `json:"level"`
}

type ShadowSocksOutbound struct {
	Servers []ShadowSocksServer `json:"servers"`
}

type SocksServer struct {
	Address string        `json:"address"` // 服务器地址，不能为空
	Port    int           `json:"port"`    // 服务器端口，不能为空
	Users   []HttpAccount `json:"users"`   // 用户列表，可以为空，为空时不进行身份验证
}

type SocksOutbound struct {
	Servers []SocksServer `json:"servers"` // 服务器列表
}

type VMessOutbound struct {
	Vnext []VMessOutboundVNext `json:"vnext"`
}

type VMessOutboundVNext struct {
	Address string                   `json:"address"`
	Port    int                      `json:"port"`
	Users   []VMessOutboundVNextUser `json:"users"`
}

type VMessOutboundVNextUser struct {
	Id       string `json:"id"`
	Level    int    `json:"level"`
	AlterId  int    `json:"alterId"`  // 固定0
	Security string `json:"security"` // 固定auto
}

type FreedomOutbound struct {
	DomainStrategy string `json:"domainStrategy,omitempty"`
	Redirect       string `json:"redirect,omitempty"`
	UserLevel      int    `json:"userLevel"`
}

func (cls *V2ray) ApplyConfig(_ context.Context) error {
	cls.LockConfig()
	defer cls.UnlockConfig()

	var content = Config{Inbounds: make([]Inbound, 0), Outbounds: make([]Outbound, 0)}

	// 入站协议
	for _, bound := range cls.GetConfig().Inbounds {
		var item = Inbound{Tag: bound.Tag}
		item.Listen = bound.Inbound.Listen
		item.Port = bound.Inbound.Port
		item.Protocol = bound.Protocol

		if bound.StreamSettings.Enable {
			item.StreamSettings = new(StreamSettings)
			item.StreamSettings.Security = "none"
			if "tls" == bound.StreamSettings.TlsSettings.Security && !bound.StreamSettings.TlsSettings.ClientOnly {
				item.StreamSettings.Security = bound.StreamSettings.TlsSettings.Security
				item.StreamSettings.TlsSettings = new(StreamTlsSettings)
				item.StreamSettings.TlsSettings.ServerName = bound.StreamSettings.TlsSettings.ServerName
			}

			item.StreamSettings.Network = string(bound.StreamSettings.Network)
			switch bound.StreamSettings.Network {
			case application.V2rayStreamSettingsNetworkTcp:
				item.StreamSettings.TcpSettings = new(StreamTcpSettings)
				item.StreamSettings.TcpSettings.Header.Type = bound.StreamSettings.TcpSettings.Header.Type
			case application.V2rayStreamSettingsNetworkKcp:
				item.StreamSettings.KcpSettings = new(StreamKcpSettings)
				item.StreamSettings.KcpSettings.Mtu = bound.StreamSettings.KcpSettings.Mtu
				item.StreamSettings.KcpSettings.Tti = bound.StreamSettings.KcpSettings.Tti
				item.StreamSettings.KcpSettings.UplinkCapacity = bound.StreamSettings.KcpSettings.UplinkCapacity
				item.StreamSettings.KcpSettings.DownlinkCapacity = bound.StreamSettings.KcpSettings.DownlinkCapacity
				item.StreamSettings.KcpSettings.ReadBufferSize = bound.StreamSettings.KcpSettings.ReadBufferSize
				item.StreamSettings.KcpSettings.WriteBufferSize = bound.StreamSettings.KcpSettings.WriteBufferSize
				item.StreamSettings.KcpSettings.Congestion = bound.StreamSettings.KcpSettings.Congestion
				item.StreamSettings.KcpSettings.Header.Type = bound.StreamSettings.KcpSettings.Header.Type
			case application.V2rayStreamSettingsNetworkWebsocket:
				item.StreamSettings.WsSettings = new(StreamWebsocketSettings)
				item.StreamSettings.WsSettings.Path = bound.StreamSettings.WebsocketSettings.Path
				item.StreamSettings.WsSettings.Headers = bound.StreamSettings.WebsocketSettings.Headers
			case application.V2rayStreamSettingsNetworkDomainSocket:
				item.StreamSettings.DsSettings = new(StreamDomainSocketSettings)
				item.StreamSettings.DsSettings.Path = bound.StreamSettings.DomainSocketSettings.Path
			case application.V2rayStreamSettingsNetworkHttp2:
				item.StreamSettings.HttpSettings = new(StreamHttp2Settings)
				item.StreamSettings.HttpSettings.Host = bound.StreamSettings.Http2Settings.Host
				item.StreamSettings.HttpSettings.Path = bound.StreamSettings.Http2Settings.Path
			case application.V2rayStreamSettingsNetworkQuic:
				item.StreamSettings.QuicSettings = new(StreamQuicSettings)
				item.StreamSettings.QuicSettings.Security = bound.StreamSettings.QuicSettings.Security
				item.StreamSettings.QuicSettings.Key = bound.StreamSettings.QuicSettings.Key
				item.StreamSettings.QuicSettings.Header.Type = bound.StreamSettings.QuicSettings.Header.Type
			}
		}

		switch bound.Protocol {
		case application.V2rayBoundProtocolDokodemoDoor:
			var dokodemo = DokodemoDoorInbound{Network: string(bound.Dokodemo.Network), Port: bound.Dokodemo.Port, Address: bound.Dokodemo.Address, Timeout: bound.Dokodemo.Timeout, FollowRedirect: bound.Dokodemo.FollowRedirect, UserLevel: bound.Dokodemo.UserLevel}

			item.Settings = dokodemo
		case application.V2rayBoundProtocolHttp:
			var http = HttpInbound{Timeout: bound.Http.Inbound.Timeout, AllowTransparent: bound.Http.Inbound.AllowTransparent, Accounts: make([]HttpAccount, 0)}
			for _, account := range bound.Http.Inbound.Accounts {
				http.Accounts = append(http.Accounts, HttpAccount{Username: account.Username, Password: account.Password})
			}

			item.Settings = http
		case application.V2rayBoundProtocolMTProto:
			var proto = MTProtoInbound{Users: []MTProtoUser{{Email: bound.MTProto.User.Email, Secret: bound.MTProto.User.Secret, Level: bound.MTProto.User.Level}}}

			item.Settings = proto
		case application.V2rayBoundProtocolShadowSocks:
			var socks = ShadowSocksInbound{Email: bound.ShadowSocks.Inbound.Email, Method: bound.ShadowSocks.Inbound.Method, Password: bound.ShadowSocks.Inbound.Password, OTA: bound.ShadowSocks.Inbound.OTA, Network: string(bound.ShadowSocks.Inbound.Network)}

			item.Settings = socks
		case application.V2rayBoundProtocolSocks:
			var socks = SocksInbound{Auth: bound.Socks.Inbound.Auth, Accounts: make([]HttpAccount, 0), UDP: bound.Socks.Inbound.UDP, Ip: bound.Socks.Inbound.Ip, UserLevel: bound.Socks.Inbound.UserLevel}
			for _, account := range bound.Socks.Inbound.Accounts {
				socks.Accounts = append(socks.Accounts, HttpAccount{Username: account.Username, Password: account.Password})
			}

			item.Settings = socks
		case application.V2rayBoundProtocolVMess:
			var vmess = VMessInbound{DisableInsecureEncryption: bound.VMess.Inbound.DisableInsecureEncryption, Clients: make([]VMessInboundClient, 0)}
			for _, client := range bound.VMess.Inbound.Clients {
				vmess.Clients = append(vmess.Clients, VMessInboundClient{Id: client.Id, Level: client.Level, AlterId: 0, Email: client.Email})
			}

			item.Settings = vmess

		default:
			return fmt.Errorf("无效或未受支持的协议: %s", bound.Protocol)
		}

		content.Inbounds = append(content.Inbounds, item)
	}

	// 出站协议
	for _, bound := range cls.GetConfig().Outbounds {
		var item = Outbound{Tag: bound.Tag}
		item.SendThrough = bound.Outbound.SendThrough
		item.Protocol = bound.Protocol

		if bound.ProxySettings.Enable {
			item.ProxySettings = new(ProxySettings)
			item.ProxySettings.Tag = bound.ProxySettings.Tag
		}

		if bound.StreamSettings.Enable {
			item.StreamSettings = new(StreamSettings)
			item.StreamSettings.Security = "none"
			if "tls" == bound.StreamSettings.TlsSettings.Security && !bound.StreamSettings.TlsSettings.ClientOnly {
				item.StreamSettings.Security = bound.StreamSettings.TlsSettings.Security
				item.StreamSettings.TlsSettings = new(StreamTlsSettings)
				item.StreamSettings.TlsSettings.ServerName = bound.StreamSettings.TlsSettings.ServerName
			}

			item.StreamSettings.Network = string(bound.StreamSettings.Network)
			switch bound.StreamSettings.Network {
			case application.V2rayStreamSettingsNetworkTcp:
				item.StreamSettings.TcpSettings = new(StreamTcpSettings)
				item.StreamSettings.TcpSettings.Header.Type = bound.StreamSettings.TcpSettings.Header.Type
			case application.V2rayStreamSettingsNetworkKcp:
				item.StreamSettings.KcpSettings = new(StreamKcpSettings)
				item.StreamSettings.KcpSettings.Mtu = bound.StreamSettings.KcpSettings.Mtu
				item.StreamSettings.KcpSettings.Tti = bound.StreamSettings.KcpSettings.Tti
				item.StreamSettings.KcpSettings.UplinkCapacity = bound.StreamSettings.KcpSettings.UplinkCapacity
				item.StreamSettings.KcpSettings.DownlinkCapacity = bound.StreamSettings.KcpSettings.DownlinkCapacity
				item.StreamSettings.KcpSettings.ReadBufferSize = bound.StreamSettings.KcpSettings.ReadBufferSize
				item.StreamSettings.KcpSettings.WriteBufferSize = bound.StreamSettings.KcpSettings.WriteBufferSize
				item.StreamSettings.KcpSettings.Congestion = bound.StreamSettings.KcpSettings.Congestion
				item.StreamSettings.KcpSettings.Header.Type = bound.StreamSettings.KcpSettings.Header.Type
			case application.V2rayStreamSettingsNetworkWebsocket:
				item.StreamSettings.WsSettings = new(StreamWebsocketSettings)
				item.StreamSettings.WsSettings.Path = bound.StreamSettings.WebsocketSettings.Path
				item.StreamSettings.WsSettings.Headers = bound.StreamSettings.WebsocketSettings.Headers
			case application.V2rayStreamSettingsNetworkDomainSocket:
				item.StreamSettings.DsSettings = new(StreamDomainSocketSettings)
				item.StreamSettings.DsSettings.Path = bound.StreamSettings.DomainSocketSettings.Path
			case application.V2rayStreamSettingsNetworkHttp2:
				item.StreamSettings.HttpSettings = new(StreamHttp2Settings)
				item.StreamSettings.HttpSettings.Host = bound.StreamSettings.Http2Settings.Host
				item.StreamSettings.HttpSettings.Path = bound.StreamSettings.Http2Settings.Path
			case application.V2rayStreamSettingsNetworkQuic:
				item.StreamSettings.QuicSettings = new(StreamQuicSettings)
				item.StreamSettings.QuicSettings.Security = bound.StreamSettings.QuicSettings.Security
				item.StreamSettings.QuicSettings.Key = bound.StreamSettings.QuicSettings.Key
				item.StreamSettings.QuicSettings.Header.Type = bound.StreamSettings.QuicSettings.Header.Type
			}
		}

		switch bound.Protocol {
		case application.V2rayBoundProtocolBlackhole:
			var blackhole = BlackholeOutbound{}
			blackhole.Response.Type = bound.Blackhole.Response.Type

			item.Settings = blackhole
		case application.V2rayBoundProtocolDns:
			var dns = DNSOutbound{Network: bound.DNS.Network, Address: bound.DNS.Address, Port: bound.DNS.Port}

			item.Settings = dns
		case application.V2rayBoundProtocolFreedom:
			var freedom = FreedomOutbound{DomainStrategy: string(bound.Freedom.DomainStrategy), Redirect: bound.Freedom.Redirect, UserLevel: bound.Freedom.UserLevel}

			item.Settings = freedom
		case application.V2rayBoundProtocolHttp:
			var http = HttpOutbound{Servers: make([]HttpServer, 0)}
			for _, server := range bound.Http.Outbound.Servers {
				var users []HttpAccount
				for _, user := range server.Users {
					users = append(users, HttpAccount{Username: user.Username, Password: user.Password})
				}

				http.Servers = append(http.Servers, HttpServer{Address: server.Address, Port: server.Port, Users: users})
			}

			item.Settings = http
		case application.V2rayBoundProtocolShadowSocks:
			var socks = ShadowSocksOutbound{Servers: make([]ShadowSocksServer, 0)}
			for _, server := range bound.ShadowSocks.Outbound.Servers {
				socks.Servers = append(socks.Servers, ShadowSocksServer{Email: server.Email, Address: server.Address, Port: server.Port, Method: server.Method, Password: server.Password, OTA: server.OTA, Level: server.Level})
			}

			item.Settings = socks
		case application.V2rayBoundProtocolSocks:
			var socks = SocksOutbound{Servers: make([]SocksServer, 0)}
			for _, server := range bound.Socks.Outbound.Servers {
				var users []HttpAccount
				for _, user := range server.Users {
					users = append(users, HttpAccount{Username: user.Username, Password: user.Password})
				}

				socks.Servers = append(socks.Servers, SocksServer{Address: server.Address, Port: server.Port, Users: users})
			}

			item.Settings = socks
		case application.V2rayBoundProtocolVMess:
			var vmess = VMessOutbound{Vnext: make([]VMessOutboundVNext, 0)}
			for _, next := range bound.VMess.Outbound.Vnext {
				var vnext = VMessOutboundVNext{Address: next.Address, Port: next.Port, Users: make([]VMessOutboundVNextUser, 0)}
				for _, user := range next.Users {
					vnext.Users = append(vnext.Users, VMessOutboundVNextUser{Id: user.Id, Level: user.Level, AlterId: 0, Security: "auto"})
				}

				vmess.Vnext = append(vmess.Vnext, vnext)
			}

			item.Settings = vmess
		default:
			return fmt.Errorf("无效或未受支持的协议: %s", bound.Protocol)
		}

		content.Outbounds = append(content.Outbounds, item)
	}

	// 保存文件
	var config, err = os.OpenFile(configFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
	if nil != err {
		return err
	}

	defer func() {
		_ = config.Close()
	}()

	if err := json.NewEncoder(config).Encode(content); nil != err {
		return err
	}

	return nil
}
